package zjua4r.googlemap.demo;

import java.util.HashMap;
import java.util.Map;

public class OffsetDecoder {	
	public static OffsetDecoder Instance = new OffsetDecoder();
	
	class Offset
	{
		public int X;
		public int Y;
		public Offset(int x, int y)
		{
			this.X = x;
			this.Y = y;
		}
	}
	
	class OffsetMap
	{
		Map<String,Offset> map = new HashMap<String, Offset>();
		public OffsetMap()
		{
			map.put("Beijing", new Offset(288,-86));
			map.put("ChangChun", new Offset(308,-156));
		}
	}
	
	static int Zoom = 16;
    static double MinLatitude = -85.05112878;
    static double MaxLatitude = 85.05112878;
    static double MinLongitude = -180;
    static double MaxLongitude = 180;
    static GSize TileSize = new GSize(256,256);
	private OffsetMap Maps = new OffsetMap();
	
	public PointLatLng ConvertToFixedLocation(double lat, double lnt)
	{

		String key = "Beijing";
		if(lat > 42)
			key = "ChangChun";
		Offset offset = Maps.map.get(key);
		GPoint gpsPoint = this.FromLatLngToPixel(lat, lnt, Zoom);
		gpsPoint.Offset(offset.X, offset.Y);
		PointLatLng fixed = this.FromPixelToLatLng(gpsPoint.X, gpsPoint.Y, Zoom);
		return fixed;
		/*
		String url = "http://www.anttna.com/goffset/goffset1.php?lat=" + lat + "&lon=" + lnt;
		
		String result = HttpUtil.queryStringForGet(url);
		
		String[] location = result.split(",");
		PointLatLng fixed = null;
		if(location.length == 2)
		{
			Double fixedLat = new Double(location[0]);
			Double fixedLnt = new Double(location[1]);
			
			fixed = new PointLatLng(fixedLat.doubleValue(), fixedLnt.doubleValue());
		}
		*/
	}
    
    private double Clip(double n, double minValue, double maxValue)
    {
       return Math.min(Math.max(n, minValue), maxValue);
    }
    
    private GPoint FromLatLngToPixel(double lat, double lng, int zoom)
    {
       GPoint ret = GPoint.Zero;

       lat = Clip(lat, MinLatitude, MaxLatitude);
       lng = Clip(lng, MinLongitude, MaxLongitude);

       double x = (lng + 180) / 360;
       double sinLatitude = Math.sin(lat * Math.PI / 180);
       double y = 0.5 - Math.log((1 + sinLatitude) / (1 - sinLatitude)) / (4 * Math.PI);

       GSize s = GetTileMatrixSizePixel(zoom);
       long mapSizeX = s.Width;
       long mapSizeY = s.Height;

       ret.X = (long)Clip(x * mapSizeX + 0.5, 0, mapSizeX - 1);
       ret.Y = (long)Clip(y * mapSizeY + 0.5, 0, mapSizeY - 1);

       return ret;
    }
    
    private PointLatLng FromPixelToLatLng(long x, long y, int zoom)
    {
       PointLatLng ret = PointLatLng.Zero;

       GSize s = GetTileMatrixSizePixel(zoom);
       double mapSizeX = s.Width;
       double mapSizeY = s.Height;

       double xx = (Clip(x, 0, mapSizeX - 1) / mapSizeX) - 0.5;
       double yy = 0.5 - (Clip(y, 0, mapSizeY - 1) / mapSizeY);

       ret.Lat = 90 - 360 * Math.atan(Math.exp(-yy * 2 * Math.PI)) / Math.PI;
       ret.Lng = 360 * xx;

       return ret;
    }   

    
    private GSize GetTileMatrixSizePixel(int zoom)
    {
       GSize s = GetTileMatrixSizeXY(zoom);
       return new GSize(s.Width * TileSize.Width, s.Height * TileSize.Height);
    }
    
    private GSize GetTileMatrixSizeXY(int zoom)
    {
       GSize sMin = GetTileMatrixMinXY(zoom);
       GSize sMax = GetTileMatrixMaxXY(zoom);

       return new GSize(sMax.Width - sMin.Width + 1, sMax.Height - sMin.Height + 1);
    }
    
    private GSize GetTileMatrixMinXY(int zoom)
    {
       return new GSize(0, 0);
    }

    private GSize GetTileMatrixMaxXY(int zoom)
    {
       long xy = (1 << zoom);
       return new GSize(xy - 1, xy - 1);
    }
}
